﻿using DistributedVisionRunner.Interface.Model;
using System;
using System.Collections.Generic;

namespace DistributedVisionRunner.Interface
{
    /// <summary>
    /// 由上位机工程师实现
    /// 此类负责转换视觉工程师(<see cref="IVisionProcessor{TData}"/>类)的输入输出
    /// </summary>
    /// <typeparam name="TOutput">
    /// 相应<see cref="IVisionProcessor{TData}"/>类的输入数据类型,
    /// 目前支持byte(单通道和3通道一样时byte), float, short, ushort
    /// </typeparam>
    public interface IVisionAdapter<TOutput>
    {
        /// <summary>
        /// 将用来命名响应的Tab
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 任何有效的ZeroMQ地址, 例如, "tcp://localhost:6000"
        /// </summary>
        string ZeroMQAddress { get; }

        /// <summary>
        /// 项目名称, 例如, 995X.
        /// 数据库的相关命名会同此挂钩
        /// </summary>
        string ProjectName { get; }



        /// <summary>
        /// 等于穴(Cavity)的个数
        /// </summary>
        int WeightSetCount { get; }


        /// <summary>
        /// extensions: 例如, new []{tif, tiff}
        /// fileTypePrompt: 例如, "tif image files"
        /// 运行图片文件时, 用来过滤掉后缀名不符的文件
        /// </summary>
        (string[] extensions, string fileTypePrompt)? ImageFileFilter { get; }

        /// <summary>
        /// 定义最终输出数据的变量名
        /// </summary>
        (string[] floatNames, string[] integerNames, string[] textNames)? ReservedOutputNames { get; }

        /// <summary>
        /// 定义图片显示的参数
        /// </summary>
        GraphicMetaData[] GraphicMetaDatas { get; }




        /// <summary>
        /// 将byte数组转换成<see cref="IVisionProcessor{TData}"/>所需的数据类型的数组
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        List<TOutput[]> ConvertInput(byte[] input);

        /// <summary>
        /// 根据视觉工程师给出的数据返回判断结果, 并且在定于有预留输出的情况下输出相应的预留输出结果
        /// </summary>
        /// <param name="rawResults">来自视觉的原始输出</param>
        /// <param name="weightedResults">经过补偿的数据输出</param>
        /// <returns></returns>
        (ResultType resultType, Statistics statistics, string errorType, string errorMessage) GetResultTypeAndReservedOutputs(StatisticsResults rawResults,
            List<DoubleData> weightedResults);

        /// <summary>
        /// 注意: 此方法内不需要再调用Directory.CreateDirectory
        /// </summary>
        /// <param name="imageData"></param>
        /// <param name="mainFolder"></param>
        /// <param name="subFolder"></param>
        /// <param name="fileNameWithoutExtension"></param>
        /// <param name="exception">null when no error occurs during image processing</param>
        void SaveImage(List<TOutput[]> imageData, string mainFolder, string subFolder, string fileNameWithoutExtension, Exception exception);

        /// <summary>
        /// 定义从文件获取图片数据的方法
        /// 获取的数据之后会输入到<see cref="IVisionProcessor{TData}"/>
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        List<TOutput[]> ReadFile(string path);

        ImageFileNamingRule ImageFileNamingRule { get; }


        string DefaultProductType { get; }

    }

    public class ImageFileNamingRule
    {
        public (char start, char end) SnSectionDelimiter { get; set; }
        public (char start, char end) CavitySectionDelimiter { get; set; }
        public (char start, char end) CreationTimeSectionDelimiter { get; set; }

        public (char start, char end) ProductSectionDelimiter { get; set; }

        public static ImageFileNamingRule Default = new ImageFileNamingRule()
        {
            SnSectionDelimiter = ('[', ']'),
            CavitySectionDelimiter = ('(', ')'),
            CreationTimeSectionDelimiter = ('@', '@'),
            ProductSectionDelimiter = ('{', '}')
        };
    }

    public class GraphicMetaData
    {
        public int Width { get; }
        public int Height { get; }
        public int RawChannels { get; }

        public GraphicMetaData(int width, int height, int rawChannels)
        {
            Width = width;
            Height = height;
            RawChannels = rawChannels;

            if (RawChannels != 1 && RawChannels != 3) throw new ArgumentException("Channel can only be 1 or 3");
        }
    }

}
